use diesel::prelude::*;
use diesel::{QueryDsl, QueryResult, SqliteConnection};

use crate::models::{NewTodo, UpdateTodo};
use crate::{models::Todo, schema::todo};

pub struct TodoMapper;

impl TodoMapper {
  pub fn find(conn: &mut SqliteConnection, id: i32) -> QueryResult<Todo> {
    todo::table.find(id).get_result::<Todo>(conn)
  }

  pub fn find_multiple(
    conn: &mut SqliteConnection,
    limit: i64,
  ) -> QueryResult<Vec<Todo>> {
    todo::table.limit(limit).load::<Todo>(conn)
  }

  pub fn create(
    conn: &mut SqliteConnection,
    new_todo: NewTodo,
  ) -> QueryResult<Todo> {
    diesel::insert_into(todo::table)
      .values(new_todo)
      .execute(conn)?;

    let last_id = Self::last_inserted_id(conn)?;

    Self::find(conn, last_id)
  }

  pub fn update(
    conn: &mut SqliteConnection,
    id: i32,
    todo: UpdateTodo,
  ) -> QueryResult<Todo> {
    diesel::update(todo::table.find(id))
      .set((
        todo::email.eq(todo.email.to_owned()),
        todo::name.eq(todo.name.to_owned()),
      ))
      .execute(conn)?;

    Self::find(conn, id)
  }

  pub fn delete(conn: &mut SqliteConnection, id: i32) -> QueryResult<usize> {
    diesel::delete(todo::table.find(id)).execute(conn)
  }

  fn last_inserted_id(conn: &mut SqliteConnection) -> QueryResult<i32> {
    todo::table
      .select(todo::id)
      .order(todo::id.desc())
      .first(conn)
  }
}
